package me.yushuai.wukong.base.service.impl;

import me.yushuai.wukong.base.api.dto.ConfigVariableBizId;
import me.yushuai.wukong.base.entity.ConfigVariable;
import me.yushuai.wukong.base.exception.NotSupportedConversion;
import me.yushuai.wukong.base.repository.ConfigVariableRepository;
import me.yushuai.wukong.base.service.ConfigVariableService;
import me.yushuai.wukong.common.exceptin.NotExistException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.Objects;
import java.util.Optional;

/**
 * 值数据类型的转换是通过Spring提供的ConversionService实现，当前业务类中使用的是{@link DefaultConversionService}创建的实例，它是一个
 * 内部专用的对象，与Spring容器内部ConversionService是不同对象。
 *
 * @author zhoushuai@189.cn
 * @since 2023-05-24
 */
@Service
public class ConfigVariableServiceImpl implements ConfigVariableService {

    private ConversionService conversionService;

    @Autowired
    private ConfigVariableRepository configVariableRepository;

    public ConfigVariableServiceImpl() {
        this.conversionService = new DefaultConversionService();
    }

    @Override
    public ConfigVariable findConfigVariableById(Long configId, Long variableId) throws NotExistException {
        return this.configVariableRepository.findById(configId, variableId)
                .orElseThrow(() -> new NotExistException(variableId, ConfigVariable.class));
    }

    @Override
    public Boolean boolValue(String configBizId, String configVariableBizId, Boolean defaultValue) {
        return this.getValue(configBizId, configVariableBizId, defaultValue, Boolean.class);
    }

    @Override
    public Boolean boolValue(String configBizId, String configVariableBizId) {
        return this.getValue(configBizId, configVariableBizId, false, Boolean.class);
    }

    @Override
    public Boolean boolValue(ConfigVariableBizId conf, Boolean defaultValue) {
        return this.getValue(conf, defaultValue, Boolean.class);
    }

    @Override
    public Boolean boolValue(ConfigVariableBizId conf) {
        return this.getValue(conf, Boolean.class);
    }

    @Override
    public Integer intValue(String configBizId, String configVariableBizId) {
        return this.getValue(configBizId, configVariableBizId, Integer.class);
    }

    @Override
    public Integer intValue(String configBizId, String configVariableBizId, int defaultValue) {
        return this.getValue(configBizId, configVariableBizId, defaultValue, Integer.class);
    }

    @Override
    public Integer intValue(ConfigVariableBizId conf) {
        return this.getValue(conf, Integer.class);
    }

    @Override
    public Integer intValue(ConfigVariableBizId conf, int defaultValue) {
        return this.getValue(conf, defaultValue, Integer.class);
    }

    @Override
    public Long longValue(String configBizId, String configVariableBizId) {
        return this.getValue(configBizId, configVariableBizId, Long.class);
    }

    @Override
    public Long longValue(String configBizId, String configVariableBizId, long defaultValue) {
        return this.getValue(configBizId, configVariableBizId, defaultValue, Long.class);
    }

    @Override
    public Long longValue(ConfigVariableBizId conf, long defaultValue) {
        return this.getValue(conf, defaultValue, Long.class);
    }

    @Override
    public Long longValue(ConfigVariableBizId conf) {
        return this.getValue(conf, Long.class);
    }

    @Override
    public String strValue(String configBizId, String configVariableBizId) {
        return this.getValue(configBizId, configVariableBizId, String.class);
    }

    @Override
    public String strValue(String configBizId, String configVariableBizId, String defaultValue) {
        return this.getValue(configBizId, configVariableBizId, defaultValue, String.class);
    }

    @Override
    public String strValue(ConfigVariableBizId conf) {
        return this.getValue(conf, String.class);
    }

    @Override
    public String strValue(ConfigVariableBizId conf, String defaultValue) {
        return this.getValue(conf, defaultValue, String.class);
    }

    @Override
    public Date dateValue(String configBizId, String configVariableBizId) {
        return this.getValue(configBizId, configVariableBizId, Date.class);
    }

    @Override
    public Date dateValue(String configBizId, String configVariableBizId, Date defaultValue) {
        return this.getValue(configBizId, configVariableBizId, defaultValue, Date.class);
    }

    @Override
    public Date dateValue(ConfigVariableBizId conf) {
        return this.getValue(conf, Date.class);
    }

    @Override
    public Date dateValue(ConfigVariableBizId conf, Date defaultValue) {
        return this.getValue(conf, defaultValue, Date.class);
    }

    @Override
    public LocalDate localDateValue(String configBizId, String configVariableBizId) {
        return this.getValue(configBizId, configVariableBizId, LocalDate.class);
    }

    @Override
    public LocalDate localDateValue(String configBizId, String configVariableBizId, LocalDate defaultValue) {
        return this.getValue(configBizId, configVariableBizId, defaultValue, LocalDate.class);
    }

    @Override
    public LocalDate localDateValue(ConfigVariableBizId conf) {
        return this.getValue(conf, LocalDate.class);
    }

    @Override
    public LocalDate localDateValue(ConfigVariableBizId conf, LocalDate defaultValue) {
        return this.getValue(conf, defaultValue, LocalDate.class);
    }

    @Override
    public LocalDateTime localDateTimeValue(String configBizId, String configVariableBizId) {
        return this.getValue(configBizId, configVariableBizId, LocalDateTime.class);
    }

    @Override
    public LocalDateTime localDateTimeValue(String configBizId, String configVariableBizId, LocalDateTime defaultValue) {
        return this.getValue(configBizId, configVariableBizId, defaultValue, LocalDateTime.class);
    }

    @Override
    public LocalDateTime localDateTimeValue(ConfigVariableBizId conf) {
        return this.getValue(conf, LocalDateTime.class);
    }

    @Override
    public LocalDateTime localDateTimeValue(ConfigVariableBizId conf, LocalDateTime defaultValue) {
        return this.getValue(conf, defaultValue, LocalDateTime.class);
    }

    @Override
    public <T> T getValue(String configBizId, String configVariableBizId, Class<T> targetType) {
        return this.getValue(configBizId, configVariableBizId, null, targetType);
    }

    @Override
    public <T> T getValue(String configBizId, String configVariableBizId, T defaultValue, Class<T> targetType) {
        final Optional<ConfigVariable> cvo = this.configVariableRepository.findByBizId(configBizId, configVariableBizId);
        if (cvo.isEmpty()) {
            return defaultValue;
        }

        final ConfigVariable configVariable = cvo.get();
        final String value = configVariable.getValue();
        if (Objects.isNull(value)) {
            return defaultValue;
        }

        if (this.conversionService.canConvert(String.class, targetType)) {
            return this.conversionService.convert(value, targetType);
        } else {
            throw new NotSupportedConversion();
        }
    }

    @Override
    public <T> T getValue(ConfigVariableBizId conf, T defaultValue, Class<T> targetType) {
        return this.getValue(conf.getConfigBizId(), conf.getConfigVariableBizId(), defaultValue, targetType);
    }

    @Override
    public <T> T getValue(ConfigVariableBizId conf, Class<T> targetType) {
        return this.getValue(conf, null, targetType);
    }

    @Override
    public <T> boolean compareValue(ConfigVariableBizId conf, T expectValue) {
        return false;
    }

    /**
     * 与配置值比较是否相同
     *
     * @param configBizId         配置
     * @param configVariableBizId 配置变量
     * @param expectValue         期望值
     * @param targetType          目标类型
     * @param <T>                 配置项目标类型
     * @return 返回true表示两个值相同否则表示两个值不同
     */
    public <T> boolean compareValue(String configBizId, String configVariableBizId, T expectValue, Class<T> targetType) {
        final T value = this.getValue(configBizId, configVariableBizId, targetType);
        return Objects.equals(expectValue, value);
    }

    /**
     * 比较配置的值与期望的值是否相同
     */
    public boolean compareIntValue(String configBizId, String configVariableBizId, Integer expectValue) {
        return this.compareValue(configBizId, configVariableBizId, expectValue, Integer.class);
    }

    /**
     * 期望的值是否包含配置值如果包含返回true否则返回false
     */
    public boolean containIntValue(String configBizId, String configVariableBizId, Integer... expectValues) {
        if (Objects.isNull(expectValues) || expectValues.length == 0) {
            return false;
        }

        final Integer value = this.getValue(configBizId, configVariableBizId, null, Integer.class);

        for (Integer expectValue : expectValues) {
            if (Objects.equals(value, expectValue)) {
                return true;
            }
        }

        return false;
    }


    /**
     * 配置值是否在给定值之间默认包含BeginValue不包含EndValue
     */
    public boolean betweenIntValue(String configBizId, String configVariableBizId, Integer beginValue, Integer endValue) {
        if (Objects.isNull(beginValue) || Objects.isNull(endValue)) {
            return false;
        }

        if (beginValue > endValue) {
            return false;
        }

        final Integer value = this.getValue(configBizId, configVariableBizId, null, Integer.class);
        if (Objects.isNull(value)) {
            return false;
        }

        return beginValue <= value && value < endValue;
    }

}
